package ch.fhnw.apsi.templates;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedHashMap;
import java.util.Objects;

import javax.annotation.Nonnull;

import ch.fhnw.apsi.Page;
import ch.fhnw.apsi.services.ServiceResult;

public class TemplateEngineImpl implements TemplateEngine {
  public TemplateEngineImpl() {
  }

  @Override
  public void writeHTML(@Nonnull final Page page, @Nonnull final Object bean, @Nonnull final Writer out)
      throws IOException {
    try {
      final URL resource = this.getClass().getResource(page.name().toLowerCase() + ".html");
      final Path path = Paths.get(resource.toURI());
      if (path == null)
        throw new Exception("resource not found: " + page);
      final StringBuilder sb = new StringBuilder(1024);
      try (BufferedReader reader = Files.newBufferedReader(path, Charset.forName("UTF-8"))) {
        final char[] buffer = new char[64];
        int len;
        while ((len = reader.read(buffer)) > 0)
          sb.append(buffer, 0, len);
      }
      final BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
      final PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
      final LinkedHashMap<String, String> map = new LinkedHashMap<>();
      for (final PropertyDescriptor propDesc : descriptors) {
        final Method getter = propDesc.getReadMethod();
        if (getter.getDeclaringClass() == Object.class)
          continue; // ignore getClass()
        if (getter.getAnnotation(Secret.class) != null)
          continue;
        final String name = propDesc.getName();
        final String value = Objects.toString(getter.invoke(bean), "");
        map.put("${" + name + "}", value);
      }

      HtmlUtils.mapReplace(sb, map);
      int indexOf = 0;
      while ((indexOf = sb.indexOf("${", indexOf)) != -1)
        sb.replace(indexOf, indexOf + 2, "<b style='color:red'>MISSING PROPERTY</b>: &#x24;{");
      out.write(sb.toString());
    } catch (final Exception cause) {
      throw new IOException(cause);
    }
  }

  @Override
  public void writeHTML(final ServiceResult result, final Writer out) throws IOException {
    this.writeHTML(result.getPage(), result.getBean(), out);
  }
}
